#include "readarguments.h"

ReadArguments::ReadArguments() {
    // default settings are set here
    input = new Input();
    amountOfPrimes=50;
    input->createPrimeInput(amountOfPrimes);
    runEvolution=true;
    usePrimes=true;
    myAmountOfEvolutionElements = 5;
    myAmountOfFormulasPerElement = 4;
    mySizeOfNewCreatedFormulas = 11;
    myAmountOfRounds=22;
    myInputFileName="";
}

void ReadArguments::printAllValues() {
    if(runEvolution) {
        std::cout   << "amount of evolution elements = " << myAmountOfEvolutionElements << std::endl
                    << "amount of formulas per element = " << myAmountOfFormulasPerElement << std::endl
                    << "amount of rounds = " << myAmountOfRounds << std::endl;
        if(usePrimes)
            std::cout << "input = the first " << amountOfPrimes << " prime numbers" << std::endl;
        else
            std::cout << "input file name = " << myInputFileName << std::endl;
        std::cout   << "size of new created formulas = " << mySizeOfNewCreatedFormulas << std::endl;
    }
}

ReadArguments::~ReadArguments() {
    delete input;
}

void ReadArguments::decryptAndExecute(const int& argc, char *argv[], char *envp[]) {
//    for (int n=0; envp[n]; n++)
//    std::cout << envp[n]<<std::endl;

    ReadArguments::readEXEName(argv);

    int position=1;
    while(position<argc) {
        if(*argv[position]=='-') {
            ReadArguments::readMinusCommand(position, argv);
        }
        else {
            ReadArguments::commandNotFound(argv[position]);
        }
        ++position;
    }
    printAllValues();
    if(runEvolution) {
        std::cout<<"\nevolution started\n";
        Evolution evolution(*input, myAmountOfEvolutionElements, myAmountOfFormulasPerElement, mySizeOfNewCreatedFormulas);
        evolution.run(myAmountOfRounds, true);
    }
}

void ReadArguments::commandNotFound(char* word) {
    runEvolution=false;
    std::cout<<"\ninvalid option '"<<word<<"'\nTry '--help' for more information.\n";
}

void ReadArguments::showHelp() {
    runEvolution=false;
    std::cout   << "\nUsage: efs [OPTION]...\n"
                << "searches matching formula (for prime-numbers by default)\n"
                << "-e --evoelements=amount\tamount of evoelements used\n"
                << "-f --formulas=amount\tamount of formulas per element\n"
                << "-i --input=fileName\tfileName of the inputFile\n"
                << "-p --primes=limit\tuse amount of prime-numbers (start with 1) as input\n"
                << "-r --rounds=limit\tamount of rounds we run\n"
                << "-s --formulasize=size\tsize of new created formulas\n"
                << "-? --help\t\tshow this help\n"
                << "\n"
                << "Example: " << exeFileName << " -iin -s3 -r10000\n"
                << "\nReport bugs to jbs" << "@" << "students.unibe.ch\n";
}

void ReadArguments::readDoubleMinusCommand(char* word) {
    if(std::strcmp(word,"help")==0) {
        ReadArguments::showHelp();
    }
    else if(std::strncmp(word,"primes=",7)==0 && isANumber(word[7])) {
        setAmountOfInputPrimes( atoi(word+7) );
    }
    else if(std::strncmp(word,"evoelements=",12)==0 && isANumber(word[12])) {
        setAmountOfEvolutionElements(atoi(word+12));
    }
    else if(std::strncmp(word,"formulas=",9)==0 && isANumber(word[9])) {
        setAmountOfFormulasPerElement(atoi(word+9));
    }
    else if(std::strncmp(word,"formulasize=",12)==0 && isANumber(word[12])) {
        setSizeOfNewCreatedFormulas(atoi(word+12));
    }
    else if(std::strncmp(word,"rounds=",7)==0 && isANumber(word[7])) {
        setAmountOfRounds(atoi(word+7));
    }
    else if(std::strncmp(word,"input=",6)==0) {
        setInputFileName(word+6);
    }
    else {
        ReadArguments::commandNotFound(word-2);
    }
}

void ReadArguments::readMinusCommand(const int& position, char *argv[]) {
    char* word = argv[position];
    if(word[1]=='-') {
        ReadArguments::readDoubleMinusCommand(word+2);
        return;
    }
    else if(word[1]=='?') {
        ReadArguments::showHelp();
    }
    else {
        if(word[1]=='p' &&  isANumber(word[2])) {
            setAmountOfInputPrimes( atoi(word+2) );
        }
        else if(word[1]=='e' &&  isANumber(word[2])) {
            setAmountOfEvolutionElements(atoi(word+2));
        }
        else if(word[1]=='f' && isANumber(word[2])) {
            setAmountOfFormulasPerElement(atoi(word+2));
        }
        else if(word[1]=='s' && isANumber(word[2])) {
            setSizeOfNewCreatedFormulas(atoi(word+2));
        }
        else if(word[1]=='r' && isANumber(word[2])) {
            setAmountOfRounds(atoi(word+2));
        }
        else if(word[1]=='i') {
            setInputFileName(word+2);
        }
        else {
            ReadArguments::commandNotFound(word);
        }
    }
}

void ReadArguments::setAmountOfInputPrimes(const unsigned int& amount) {
    if(amount < 1) {
        std::stringstream out;
        out << "Couldn't set amount of input primes to " << amount <<". Out of range.";
        std::string s = out.str();
        printError(s);
    }
        else {
        delete input;
        input = new Input();
        input->createPrimeInput( amount );
//        std::cout<<"Amount of input primes set to "<< amount << ".\n";
        usePrimes=true;
        amountOfPrimes = amount;
    }
}

bool ReadArguments::isANumber(const char& c) {
    return (47 < c) && (c < 58);
}

void ReadArguments::printError(std::string& s) {
    runEvolution=false;
    std::cout << "ERROR: " << s << std::endl;
}

void ReadArguments::setAmountOfEvolutionElements(const unsigned int& amount){
    if(amount < 1) {
        std::stringstream out;
        out << "Couldn't set amount of evolution elements to " << amount <<". Out of range.";
        std::string s = out.str();
        printError(s);
    }
    else {
        myAmountOfEvolutionElements=amount;
//        std::cout<<"Amount of evolution elements set to "<< myAmountOfEvolutionElements << ".\n";
    }
}

void ReadArguments::setAmountOfFormulasPerElement(const unsigned int& amount) {
    if(amount < 2) {
        std::stringstream out;
        out << "Couldn't set amount of formulas per Element to " << amount <<". Should be >= 2.";
        std::string s = out.str();
        printError(s);
    }
    else {
        myAmountOfFormulasPerElement=amount;
//        std::cout<<"Amount of formulas per Element set to "<< myAmountOfFormulasPerElement << ".\n";
    }
}

void ReadArguments::setSizeOfNewCreatedFormulas(const unsigned int& size) {
    if(size < 1) {
        std::stringstream out;
        out << "Couldn't set size of new created formulas to " << size << ". Out of range.";
        std::string s = out.str();
        printError(s);
    }
    else if(size%2 == 0) {
        std::stringstream out;
        out << "Couldn't set size of new created formulas to " << size <<". It has to be an odd/uneven number.";
        std::string s = out.str();
        printError(s);
    }
    mySizeOfNewCreatedFormulas=size;
//    std::cout<<"Size of new created formulas set to " << size << ".\n";
}

void ReadArguments::setAmountOfRounds(const unsigned int& rounds) {
    if(rounds < 1) {
        std::stringstream out;
        out << "Couldn't set amount of rounds to " << rounds << ". Out of range.";
        std::string s = out.str();
        printError(s);
    }
    myAmountOfRounds=rounds;
//    std::cout<<"Amount of rounds set to " << rounds << ".\n";
}

void ReadArguments::setInputFileName(const char* name) {
    myInputFileName = name;
    delete input;
    input = new Input();
    input->importFromTextfile(name);
    usePrimes=false;
//    std::cout<<"Input file name set to \""<< name << "\".\n";
}

void ReadArguments::readEXEName(char *argv[]) {
    exeFileName = argv[0];
}
